"""
chord.db

Provides the Chord data abstraction over the Django database API.
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, 'Finger from jackson.chord.models')
from jackson.chord.models import Finger

debug.importing(__name__, 'Server from jackson.chord')
from jackson.chord import Server

debug.importing(__name__, 'settings from django.conf', 2)
from django.conf import settings

debug.importing(__name__, 'key_in_range from jackson.utils')
from jackson.utils import key_in_range

debug.post_import(__name__)

### End of imports

def clear_db():
    """ Empties database tables.
    @author: ep
    @rtype: None
    """
    context = [__name__, 'clear_db()']
    debug.enter(context, None, 3)
    Finger.objects.all().delete()
    debug.leave(context, None, 3)

def closest_preceding_finger(key):
    """ Returns a chord.Server object representing the closest finger preceding the given key.
    @author: ep
    @param key: a key
    @rtype: chord.Server
    @todo: improve db access efficiency (may be more efficient to load whole table with 1 query than use multiple queries)
    """
    context = [__name__, 'closest_preceding_finger()']
    debug.enter(context, { 'key' : key }, 3)
    #print "get_me in db"
    my_key = get_me().key

    # Search from the end until we find a finger between this server and key
    k = settings.M
    server = get_finger(k)
#    if (k >= 0) or (not key_in_range(server.key, my_key, key)):
#        debug.comment(context,'!!!!!!!!!!!!!!!!there is a serious problem with the function.')
    while (k > 0) and (not key_in_range(server.key, my_key, key)):
        k = k - 1
        server = get_finger(k)
    
    # At this point, server is the finger we were looking for.
    debug.leave(context, { 'server' : server }, 3)
    return server

def get_finger(k):
    """ Returns a chord.Server object representing finger k.
    @author: ep
    @rtype: chord.Server
    """
    context = [__name__, 'get_finger()']
    debug.enter(context, { 'k' : k }, 3)
    # Simple bounds check
    if not (-1 <= k <= settings.M):
        debug.comment(context, 'Bounds check failed, returning None')
        debug.leave(context, None, 3)
        return None
    ans = Server(finger=Finger.objects.get(i = k))
    debug.leave(context, { 'ans' : ans }, 3)
    return ans

def get_me():
    """ Returns a chord.Server object representing this server.
    @author: ep
    """
    context = [__name__, 'get_me()']
    debug.enter(context, None, 3)
    ans = get_finger(0)
    debug.leave(context, { 'ans' : ans }, 3)
    return ans

def get_predecessor():
    """ Returns a chord.Server object representing this server's predecessor.
    @author: ep
    """
    context = [__name__, 'get_me()']
    debug.enter(context, None, 3)
    ans = get_finger(-1)
    debug.leave(context, { 'pred' : ans }, 3)
    return ans

def get_successor():
    """ Returns a chord.Server object representing this server's successor.
    @author: ep
    """
    context = [__name__, 'get_successor()']
    debug.enter(context, None, 3)
    ans = get_finger(1)
    debug.leave(context, { 'succ' : ans }, 3)
    return ans

def i_am(server):
    """ Returns True if the given Server object represents this server
    @author: ep
    """
    context = [__name__, 'i_am()']
    debug.enter(context, { 'server' : server }, 3)
    # Lookup my information
    #print "get_me in db"
    me = get_me()
    
    if (me.ip == server.ip) and (me.port == server.port) and (me.key == server.key):
        flag = True
    else:
        flag = False
        
    debug.leave(context, { 'is_me?' : flag }, 3)
    return flag

def i_am_active():
    """ Returns True if my status is 'A', False otherwise
    @author: ep
    """
    context = [__name__, 'i_am_active()']
    debug.enter(context, None, 4)
    try:
        #print "get_me in db"
        ans = (get_me().status == 'A')
    except Exception:
        ans = False
    debug.leave(context, { 'i_am_active' : ans }, 4)
    return ans

def i_am_leaving():
    """ Returns True if my status is 'L', False otherwise
    @author: ep
    """
    context = [__name__, 'i_am_leaving()']
    debug.enter(context, None, 4)
    #print "get_me in db"
    ans = get_me().status == 'L'
    debug.leave(context, { 'ans' : ans }, 4)
    return ans

def init_fingers(me):
    context = [__name__, 'init_fingers()']
    debug.enter(context, { 'me' : me }, 3)
    # Initialize my finger table - every entry should be me, activated
    me2 = me
    me2.status = 'A'
    for k in range(-1, settings.M + 1):
        debug.comment(context, 'Initializing finger ' + str(k), 4)
        set_finger(k, me2)
    debug.leave(context, None, 3)

def set_finger(k, new_server):
    """ Sets finger k to the given server.
    @author: ep
    """
    context = [__name__, 'set_finger()']
    debug.enter(context, { 'finger' : k, 'new_value' : new_server }, 3)
    if new_server == None:
        debug.comment(context, 'Passed a null server, returning None, not adding to the db')
        debug.leave(context, None, 3)
        return None
    if not (-1 <= k <= settings.M):
        debug.comment(context, 'Bounds check failed, returning None, not adding to the db')
        debug.leave(context, None, 3)
        return None

    # Update the desired finger
    finger = Finger()
    finger.i = k
    finger.ip = new_server.ip
    finger.port = new_server.port
    finger.key = new_server.key
    finger.status = new_server.status

    # Save changes to the database
    finger.save()
    debug.leave(context, None, 3)

def update_predecessor(server):
    """ Sets predecessor entry in the finger table to given server.
    @author: ep
    @rtype: None
    """
    context = [__name__, 'update_predecessor()']
    debug.enter(context, { 'new_pred' : server }, 3)
    set_finger(-1, server)
    debug.leave(context, None, 3)
    
def update_status(status):
    """ Sets this server's status.
    @author: ep
    @rtype: None
    """
    context = [__name__, 'update_status()']
    debug.enter(context, { 'status' : status}, 3)
    me = Finger.objects.get(i = 0)
    me.status = status
    me.save()
    debug.leave(context, None, 3)

def update_successor(server):
    """ Sets successor entry in the finger table to given server.
    @author: ep
    @rtype: None
    """
    context = [__name__, 'update_successor()']
    debug.enter(context, { 'new_succ' : server }, 3)
    set_finger(1, server)
    debug.leave(context, None, 3)
